/**
 * 
 */
package com.ofek.eventos.net.lks;

import java.util.ArrayList;
import java.util.Map.Entry;

/**
 * @author Ofek
 *
 */
public class ServerDatabaseHandle<ID, T> extends ClientDatabaseHandle<ID, T> {
	/**
	 * @param pendingSyncItems
	 * @param syncedItems
	 * @param pendingRemoval
	 */
	public ServerDatabaseHandle() {
		super(new HashMap<ID, ItemVersion<ID, T>>(), new HashMap<ID,ItemVersion<ID, T>>(), new HashMap<ID, ItemVersion<ID, T>>());
	}
	private final HashMap<ID,LKWhoKnowsWhatLog<ID>> whoKnowsWhat = new HashMap<ID,LKWhoKnowsWhatLog<ID>>();



	/* (non-Javadoc)
	 * @see com.ofek.eventos.net.lks.DatabaseHandle#getPendings(com.ofek.eventos.net.lks.Sync)
	 */
	@Override
	public ArrayList<ItemVersion<ID, T>> getPendings(final Sync<ID, T> sync) {
		final LastKnownHandler<ID> lastKnowns = sync.lastKnowns;
		for (final Entry<ID,Long> versionKnownForID : lastKnowns) {
			final ID key = versionKnownForID.getKey();
			LKWhoKnowsWhatLog<ID> lkLog = whoKnowsWhat.get(key);
			if ( lkLog==null )  
				whoKnowsWhat.put(key,lkLog=new LKWhoKnowsWhatLog<ID>(key));
			final Long version = versionKnownForID.getValue();
			if ( lkLog.highestVersion < version) 
				lkLog.highestVersion = version;
			lkLog.whoKnowsAboutIt.add(sync.sender);
			lkLog.whoKnowsWhatVersion.put(sync.sender, version);
		}


		final ArrayList<ItemVersion<ID, T>> res = new ArrayList<ItemVersion<ID,T>>();


		for ( final ItemVersion<ID,T> item : syncedItems.values()  ) 
			if (!lastKnowns.isKnown(item.id, item.version)) 
				res.add(item);


		for ( final ItemVersion<ID,T> item : pendingRemoval.values()  ) 
			if ( shouldKnowAbout( item.id,sync.sender ) ) 
				res.add(item);




		return res;
	}
	/* (non-Javadoc)
	 * @see com.ofek.eventos.net.lks.ClientDatabaseHandle#syncItems(com.ofek.eventos.net.lks.Sync)
	 */
	@Override
	public LastKnownHandler<ID> syncItems(final Sync<ID, T> sync) {
		final ArrayList<ItemVersion<ID, T>> waitingItems = new ArrayList<ItemVersion<ID,T>>();
		final LastKnownHandler<ID> filteredLK = filteredLK(sync);
		if ( sync.newItems==null ) return filteredLK;
		super.syncItems(sync);
		for ( final ItemVersion<ID,T> item : pendingRemoval.values()  ) {
			if (whoKnowsWhat.get(item.id).isRemovalAllowed()) { 
				waitingItems.add(item);
			}

		}
		for (final ItemVersion<ID, T> item : waitingItems) {
			syncedItems.remove(item.id);
			pendingRemoval.remove(item.id);
			removedLastKnowns.forget(item.id);
			lastKnownHandler.forget(item.id);
			fireItemRemoved(item);
		}
		return filteredLK;
	}


	/**
	 * @param sync
	 * @return
	 */
	private LastKnownHandler<ID> filteredLK(final Sync<ID, T> sync) {
		final LastKnownHandler<ID> toSend = (LastKnownHandler<ID>) aliveLastKnowns.clone();
		for (final ItemVersion<ID, T> item : pendingRemoval.values()) {
			if ( sync.lastKnowns.isKnown(item.id, item.version) || shouldKnowAbout( item.id,sync.sender ) ) 
				toSend.updateIfNeeded(item.id, item.version);
		}
		return toSend;
	}
	/**
	 * @param item
	 * @param sync
	 * @return
	 */
	private boolean shouldKnowAbout(final ID itemId, final String sender) {
		final LKWhoKnowsWhatLog<ID> lkWhoKnowsWhatLog = whoKnowsWhat.get(itemId);
		final java.util.HashMap<String, Long> whoKnowsWhatVersion = lkWhoKnowsWhatLog.whoKnowsWhatVersion;
		final Long ver = whoKnowsWhatVersion.get(sender);
		return ver==null ? false : ver.compareTo(lkWhoKnowsWhatLog.highestVersion)<0;
	}
	/* (non-Javadoc)
	 * @see com.ofek.eventos.net.lks.DatabaseHandle#toString()
	 */
	@Override
	public String toString() {
		return super.toString() + "\n" + whoKnowsWhat;
	}
}
